/**
 * CMOSTEK Sub-G 射频芯片 SPI 接口驱动
 * Copyright (c) 2021 深圳市智辰科技有限公司
 * All rights reserved.
 */

#include "nonos.h"
#include "sc7a20.h"

#define LOGTAG "SC7A20"

#define csn(level)  (dev->csn(level))
#define sclk(level) (dev->sclk(level))
#define mosi(level) (dev->mosi(level))
#define miso()      (dev->miso())

#define delay()

#define DUMMY_BYTE 0

static uint8_t spiTxRx(driver_sc7a20_t *dev, uint8_t ch) {
	uint8_t inch = 0;

	for(uint8_t i=0; i<8; i++) {
		sclk(0); delay();	// SCLK 低时发送数据
		mosi(ch & 0x80);	// 发送数据(MSB:高位在前)
		ch <<= 1;			// 准备发送下一个bit(MSB:高位在前)
		
		sclk(1); delay();	// SCLK 高时接收数据(MSB:高位在前)
		inch <<= 1;			// 准备接收当前bit(MSB:高位在前)
		inch += miso();		// 接收当前bit
	}

	return inch;
}

static const sc7a20_reg_t _regs[] = {
	{ 0x20, 0x47 }, // 50 Hz, 低功耗模式，使能x/y/z轴
	{ 0x21, 0x04 }, // HPCLICK=1：CLICK 功能高通滤波使能
	{ 0x23, 0x80 }, // BDU=1：输出数据寄存器不更新直到 MSB 和 LSB 被读取
	{ 0x25, 0x80 }, // CLICK=1：中断在 INT2 脚上
	{ 0x3A, 0x15 }, // 单双击触发的阈值(越小越灵敏)
	{ 0x3B, 0x10 }, // 单击检测时间的阈值
	{ 0x3C, 0x05 }, // 延迟时间
	{ 0x38, 0x15 }, // 0b00010101: X/Y/Z 轴单击中断使能；X/Y/Z 轴双击中断禁用
	{ 0xFF, 0xFF }
};

void sc7a20_init(driver_sc7a20_t *dev, const sc7a20_reg_t *regs) {
	if(!dev) { return; }
	if(!regs) { regs = _regs; }
	while(regs->addr != 0xFF) {
		sc7a20_write_reg(dev, regs->addr, regs->value); regs++;
	}
}

bool sc7a20_exists(driver_sc7a20_t *dev) {
	return sc7a20_read_reg(dev, 0x0F) == 0x11;
}

void sc7a20_write_reg(driver_sc7a20_t *dev, uint8_t addr, uint8_t ch) {
    csn(0);
	// 发送寄存器地址
	BitReset(addr, 7);	// Bit7=0表示写
	BitReset(addr, 6);	// Bit6=0表示单个寄存器操作
    spiTxRx(dev, addr);
	// 发送寄存器值
    spiTxRx(dev, ch);
	csn(1);
}

uint8_t sc7a20_read_reg(driver_sc7a20_t *dev, uint8_t addr) {
    csn(0);
	// 发送寄存器地址
	BitSet(addr, 7);	// Bit7=1表示读
	BitReset(addr, 6); 	// Bit6=0表示单个寄存器操作
    spiTxRx(dev, addr);
	// 读寄存器值
    uint8_t ch = spiTxRx(dev, DUMMY_BYTE);
	csn(1);
	return ch;
}

void sc7a20_write_regs(driver_sc7a20_t *dev, uint8_t addr, const uint8_t* buf, uint16_t len) {
    csn(0);
	
	// 发送寄存器地址
	BitReset(addr, 7);	// Bit7=0表示写
	BitSet(addr, 6); 	// Bit6=1表示寄存器地址递增
    spiTxRx(dev, addr);
	
	// 发送寄存器值
    for(uint16_t i=0; i<len; i++) {
        spiTxRx(dev, buf[i]);
    }
	
    csn(1);
}

void sc7a20_read_regs(driver_sc7a20_t *dev, uint8_t addr, uint8_t* buf, uint16_t len) {
    csn(0);
	
	// 发送寄存器地址
	BitSet(addr, 7);	// Bit7=1表示读
	BitSet(addr, 6); 	// Bit6=1表示寄存器地址递增
    spiTxRx(dev, addr);
	
	// 读寄存器值
    for(uint16_t i=0; i<len; i++) {
        buf[i] = spiTxRx(dev, DUMMY_BYTE);
    }
	
    csn(1);
}

void sc7a20_read_acc_xyz(driver_sc7a20_t *dev, s16 xyz[3]) {
	// 读取当前量程
	uint8_t reg4 = sc7a20_read_reg(dev, GS_REG_CTRL4);
	gs_reg_ctrl4_t *ctrl4 = (gs_reg_ctrl4_t*)&reg4;
	int range = (ctrl4->FS + 1)*100L;
	// 读取加速度数值
	sc7a20_read_regs(dev, GS_REG_OUT_X, (uint8_t*)xyz, 6);
	// 换算成 0.01G
	xyz[0] = xyz[0]*range >> 14;
	xyz[1] = xyz[1]*range >> 14;
	xyz[2] = xyz[2]*range >> 14;
}

uint32_t sc7a20_read_acc2(driver_sc7a20_t *dev) {
	s16 xyz[3];
	sc7a20_read_acc_xyz(dev, xyz);
	return xyz[0]*xyz[0] + xyz[1]*xyz[1] + xyz[2]*xyz[2];
}

void sc7a20_set_acc_trigger(driver_sc7a20_t *dev, uint32_t acc, sc7a20_acc2_trig_handler_t handler, void *cookie) {
	dev->acc2TrigLevel = acc * acc;
	dev->acc2TrigHandler = handler;
	dev->acc2TrigHandlerCookie = cookie;
}

void sc7a20_int1Handler(driver_sc7a20_t *dev) {
	log("g1");
}

void sc7a20_int2Handler(driver_sc7a20_t *dev) {
	// 读取当前中断状态
	//uint8_t aoi1  = sc7a20_read_reg(dev, GS_REG_AOI1_SRC);
	//uint8_t aoi2  = sc7a20_read_reg(dev, GS_REG_AOI2_SRC);
	//uint8_t click = sc7a20_read_reg(dev, GS_REG_CLICK_SRC);
	//log("%02X, %02X, %02X\r\n", aoi1, aoi2, click);

	uint acc2 = sc7a20_read_acc2(dev);
	//log("acc2 = %d / %d\r\n", acc2, dev->acc2TrigLevel);
	if(dev->acc2TrigHandler && acc2 > dev->acc2TrigLevel) {
		dev->acc2TrigHandler(dev, acc2, dev->acc2TrigHandlerCookie);
	}
}

void sc7a20_test(driver_sc7a20_t *dev) {
	uint8_t regs[0x3D];		
	logd("WHO_AM_I: 0x%02X\r\n", sc7a20_read_reg(dev, 0x0F));
		
	s16 xyz[3];
	sc7a20_read_acc_xyz(dev, xyz);
	log("xyz=[%d, %d, %d]\r\n", xyz[0], xyz[1], xyz[2]);
	
	uint acc2 = sc7a20_read_acc2(dev);
	log("acc2 = %d / %d\r\n", acc2, dev->acc2TrigLevel);
	
	sc7a20_read_regs(dev, 0, regs, sizeof(regs));
	dumpBin(regs, sizeof(regs), 16, true, false);
}
